Skip to content

Fixes #25666: Add retry-with-backoff to getServiceStatus for transient failure recovery#27112

Open
RajdeepKushwaha5 wants to merge 5 commits intoopen-metadata:mainfrom
RajdeepKushwaha5:fix/25666-airflow-status-retry-on-transient-errors
Open

Fixes #25666: Add retry-with-backoff to getServiceStatus for transient failure recovery#27112
RajdeepKushwaha5 wants to merge 5 commits intoopen-metadata:mainfrom
RajdeepKushwaha5:fix/25666-airflow-status-retry-on-transient-errors

Conversation

@RajdeepKushwaha5
Copy link
Copy Markdown
Contributor

@RajdeepKushwaha5 RajdeepKushwaha5 commented Apr 7, 2026

Describe your changes:

Fixes #25666

Problem: PipelineServiceClient.getServiceStatus() delegates to getServiceStatusInternal() with zero retry logic. A single transient REST failure (e.g., selector manager closed during an Airflow restart or network blip) immediately returns an unhealthy response (code 500), causing OpenMetadata to mark the Airflow agent as UNAVAILABLE. The agent never auto-recovers — a manual UI refresh or service restart is required.

Root Cause: While a getServiceStatusBackoff() method with Resilience4j retry already existed in PipelineServiceClient, it was never called by any production code. Both callers (IngestionPipelineResource.getRESTStatus() and SystemRepository.getPipelineServiceClientValidation()) call getServiceStatus() directly, which had no retry.

Fix: Move the Resilience4j retry-with-backoff logic into getServiceStatus() itself, so every health check benefits from transient failure tolerance:

  • getServiceStatus() now retries up to 3 attempts with 5-second backoff intervals when the response code is not 200
  • getServiceStatusBackoff() is simplified to delegate to getServiceStatus() (avoids nested/double retry)
  • Removed the unused Supplier import

Test: Added getServiceStatusRecoversFromTransientFailure test that enqueues a transient 500 followed by a healthy 200 and verifies the final status is healthy with exactly 3 requests (detection + failed attempt + successful retry).

Type of change:

  • Bug fix
  • Improvement
  • New feature
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation

Checklist:

  • I have read the CONTRIBUTING document.
  • My PR title is Fixes <issue-number>: <short explanation>
  • I have commented on my code, particularly in hard-to-understand areas.
  • For JSON Schema changes: I updated the migration scripts or explained why it is not needed.
  • I have added a test that covers the exact scenario we are fixing. For complex issues, comment the issue number in the test for future reference.

Summary by Gitar

  • Refactored configuration handling:
    • Introduced getStringParam and getIntParam helpers in AirflowRESTClient to safely parse configuration parameters.
    • Added explicit validation for username and password in the constructor, throwing PipelineServiceClientException if missing.
  • Improved constructor robustness:
    • Added support for various timeout types and handled null configurations or missing additionalProperties maps gracefully.
  • Expanded test coverage:
    • Added comprehensive tests for parameter handling, including non-numeric timeouts and missing credentials scenarios.

This will update automatically on new commits.

Copilot AI review requested due to automatic review settings April 7, 2026 02:51
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves ingestion agent health-check resilience by adding Resilience4j retry-with-backoff to PipelineServiceClient.getServiceStatus(), so transient pipeline-service failures (e.g., during Airflow restarts) don’t immediately mark the agent as unavailable.

Changes:

  • Moved retry-with-backoff logic into PipelineServiceClient.getServiceStatus() and simplified getServiceStatusBackoff() to delegate.
  • Updated retry to operate on PipelineServiceClientResponse (retrying when HTTP code is non-200).
  • Added an Airflow REST client test ensuring a transient 500 followed by 200 recovers successfully and issues the expected number of requests.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
openmetadata-spec/src/main/java/org/openmetadata/service/clients/pipeline/PipelineServiceClient.java Centralizes retry-with-backoff in getServiceStatus() so all callers benefit from transient failure tolerance.
openmetadata-service/src/test/java/org/openmetadata/service/clients/pipeline/airflow/AirflowRESTClientTest.java Adds regression test verifying recovery from a transient health-check failure.

@RajdeepKushwaha5 RajdeepKushwaha5 force-pushed the fix/25666-airflow-status-retry-on-transient-errors branch from 68791c8 to 9336512 Compare April 7, 2026 03:30
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Copilot AI review requested due to automatic review settings April 7, 2026 04:54
@RajdeepKushwaha5 RajdeepKushwaha5 force-pushed the fix/25666-airflow-status-retry-on-transient-errors branch from 9336512 to f32ab33 Compare April 7, 2026 04:54
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

@RajdeepKushwaha5 RajdeepKushwaha5 force-pushed the fix/25666-airflow-status-retry-on-transient-errors branch from f32ab33 to 0d023dc Compare April 7, 2026 05:38
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Copilot AI review requested due to automatic review settings April 7, 2026 05:49
@RajdeepKushwaha5 RajdeepKushwaha5 force-pushed the fix/25666-airflow-status-retry-on-transient-errors branch from 0d023dc to 7a38087 Compare April 7, 2026 05:49
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

@RajdeepKushwaha5 RajdeepKushwaha5 force-pushed the fix/25666-airflow-status-retry-on-transient-errors branch from 7a38087 to a6412ae Compare April 7, 2026 06:00
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Copilot AI review requested due to automatic review settings April 7, 2026 06:12
@RajdeepKushwaha5 RajdeepKushwaha5 force-pushed the fix/25666-airflow-status-retry-on-transient-errors branch from a6412ae to 2676e07 Compare April 7, 2026 06:12
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings April 19, 2026 18:26
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment on lines 216 to +221
public PipelineServiceClientResponse getServiceStatus() {
if (pipelineServiceClientEnabled) {
return getServiceStatusInternal();
if (!pipelineServiceClientEnabled) {
return buildHealthyStatus(DISABLED_STATUS).withPlatform(DISABLED_STATUS);
}
PipelineServiceClientResponse response =
retryForServiceStatus().executeSupplier(this::getServiceStatusInternal);
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description says getServiceStatus() now retries when the response code is “not 200”, but the implementation only retries on null or code >= 500. Please align the PR description with the actual behavior, or adjust the retry predicate if the intended behavior is to retry all non-200 responses.

Copilot uses AI. Check for mistakes.
@gitar-bot
Copy link
Copy Markdown

gitar-bot Bot commented Apr 20, 2026

Code Review ✅ Approved 10 resolved / 10 findings

Implements robust retry-with-backoff logic for service status checks, resolving multiple NPEs, configuration flaws, and incorrect exception handling. All identified issues were addressed, ensuring stable and reliable recovery from transient failures.

✅ 10 resolved
Bug: GetEntityVersionsTool calls non-existent CommonUtils.parseIntParam()

📄 openmetadata-mcp/src/main/java/org/openmetadata/mcp/tools/GetEntityVersionsTool.java:51
At line 51, CommonUtils.parseIntParam(params.get("limit"), DEFAULT_LIMIT) is called, but the CommonUtils class in the MCP tools package does not define a parseIntParam() method. This will cause a compilation error.

Bug: CompareEntityVersionsTool NPE when pojoToJson returns null

📄 openmetadata-mcp/src/main/java/org/openmetadata/mcp/tools/CompareEntityVersionsTool.java:115-117
JsonUtils.pojoToJson(null) returns null. At line 117, fromJson.equals(toJson) will throw a NullPointerException if either fromVal or toVal is null (e.g., a field not present in one version). This is a reachable condition since version comparisons commonly involve fields that were added or removed.

Bug: CompareEntityVersionsTool hardcodes table-specific fields

📄 openmetadata-mcp/src/main/java/org/openmetadata/mcp/tools/CompareEntityVersionsTool.java:97-100
The computeDifferences method compares a hardcoded list of fields (columns, tableConstraints, tableType, etc.) that are specific to Table entities. When comparing other entity types (Pipeline, Dashboard, Topic, etc.), this tool will miss all entity-specific fields and only compare the few common ones (description, owners, tags, displayName). The tool's name and parameters suggest it is generic.

Edge Case: MCP tools NPE when exception message is null

📄 openmetadata-mcp/src/main/java/org/openmetadata/mcp/tools/CompareEntityVersionsTool.java:90 📄 openmetadata-mcp/src/main/java/org/openmetadata/mcp/tools/GetEntityVersionsTool.java:72
Both CompareEntityVersionsTool (line 90) and GetEntityVersionsTool (line 72) catch all exceptions and call Map.of("error", e.getMessage()). Map.of() does not permit null values, so if e.getMessage() is null (common for NPE, ClassCastException, etc.), this will throw a NullPointerException, masking the original error.

Quality: PR includes unrelated files (notes, scripts, issue templates)

📄 TOP_10_CONTRIBUTIONS.txt 📄 TOP_10_CONTRIBUTIONS_V2.txt 📄 issue-assignment-requests.md 📄 ISSUE_DESCRIPTION.md 📄 ISSUE_DESCRIPTION_RESOURCE_LEAKS.md 📄 ISSUE_SUBJECT_CONTEXT_SILENT_CATCHES.md 📄 PR_DESCRIPTION.md 📄 PR_DESCRIPTION_AIRFLOW_RETRY.md 📄 PR_DESCRIPTION_RESOURCE_LEAKS.md 📄 scripts/fix_basic.py 📄 openmetadata-mcp/src/main/java/org/openmetadata/mcp/tools/CompareEntityVersionsTool.java 📄 openmetadata-mcp/src/main/java/org/openmetadata/mcp/tools/GetEntityVersionsTool.java
The PR includes 12 files that are unrelated to the stated fix (retry-with-backoff for getServiceStatus): TOP_10_CONTRIBUTIONS.txt, TOP_10_CONTRIBUTIONS_V2.txt, issue-assignment-requests.md, ISSUE_DESCRIPTION.md, ISSUE_DESCRIPTION_RESOURCE_LEAKS.md, ISSUE_SUBJECT_CONTEXT_SILENT_CATCHES.md, PR_DESCRIPTION*.md, scripts/fix_basic.py, and the two MCP tool files. These appear to be personal working notes and unrelated feature additions that should be in separate PRs. The fix_basic.py script contains a hardcoded Windows path (D:\OpenMetadata\...).

...and 5 more resolved from earlier reviews

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AirflowRESTClient marks agent UNAVAILABLE on transient error (selector manager closed) and does not auto-recover

3 participants